package middleware

import (
	"bytes"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/goinggo/mapstructure"
	"github.com/pkg/errors"
	"io/ioutil"
	"zimuzu_cvn_web_api/config"
	. "zimuzu_cvn_web_api/pkg/e"
	"zimuzu_cvn_web_api/pkg/errno"
	"zimuzu_cvn_web_api/router/api"
	"zimuzu_cvn_web_api/service"
	"zimuzu_cvn_web_api/service/common"
)

var limiter = common.NewIPRateLimiter(1, 5)

// IP限制中间件
func IPRateLimiter() gin.HandlerFunc {
	return func(c *gin.Context) {
		limiter := limiter.GetLimiter(c.ClientIP())
		if !limiter.Allow() {
			Log("err", "IPRateLimiter Err:")
			api.SendResponse(c, errno.IllegalErr, nil)
			c.Abort()
			return
		}
		c.Next()
	}
}

func TrackLog() gin.HandlerFunc {
	return func(c *gin.Context) {
		Log("Info", " [Start Function]: ", c.Request.URL)
		bodyBytes, _ := ioutil.ReadAll(c.Request.Body)
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
		bodyStr := string(bodyBytes)
		Log("Info", " [PARAMS_POST]: ", bodyStr)
		c.Next()
	}
}

//验证token中间件
func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		if err := CheckTokenLogin(c); err != nil {
			api.SendResponse(c, err, nil)
			c.Abort()
			return
		}
		c.Next()
	}
}

//校验内部人员
func CheckInsider() gin.HandlerFunc {
	return func(c *gin.Context) {
		uid, _ := service.GetUserIdByRequest(c)
		if ok := service.IsInsider(uid); !ok {
			api.SendResponse(c, errors.New("非法请求"), nil)
			c.Abort()
			return
		}
		c.Next()
	}
}

//校验开发者
func CheckInDevelopment() gin.HandlerFunc {
	return func(c *gin.Context) {
		uid, _ := service.GetUserIdByRequest(c)
		if ok := service.IsInDevelopment(uid); ok {
			api.SendResponse(c, errors.New("维护中..."), nil)
			c.Abort()
			return
		}
		c.Next()
	}
}

func parseToken(s string) (*jwt.Token, error) {
	fn := func(token *jwt.Token) (interface{}, error) {
		return []byte(config.C.JwtSecret), nil
	}
	return jwt.Parse(s, fn)
}

func CheckTokenLogin(c *gin.Context) error {
	//从header中获取token
	tokenStr := c.Request.Header.Get("Authorization")
	if len(tokenStr) == 0 {
		errno.LoginOut.Info = "登陆无效,请重新登录"
		return errno.LoginOut
	}
	//获取验证之后的结果
	token, err := parseToken(tokenStr)
	if err != nil {
		Log("Info", "", tokenStr)
		errno.LoginOut.Info = "token格式错误"
		return errno.LoginOut
	}
	//如果验证结果是false直接返回token错误我 如果成功则继续下一个handler
	if token.Valid {
		claim := service.TokenClaims{}
		mapstructure.Decode(token.Claims.(jwt.MapClaims), &claim)
		loginToken, err := service.GetLoginTokenCache(claim.Uid, service.LoginPlatformApp)
		if err != nil {
			errno.LoginOut.Info = "请重新登录"
			return errno.LoginOut
		}
		if tokenStr != loginToken && len(loginToken) != 0 {
			errno.LoginOut.Info = "账号已在其他设备登录，请重新登录"
			return errno.LoginOut
		}
		c.Set("userId", claim.Uid)
		return nil
	} else {
		errno.LoginOut.Info = "验证错误,请重新登录"
		return errno.LoginOut
	}
}
